home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NetNews Offline 2
/
NetNews Offline Volume 2.iso
/
news
/
comp
/
std
/
c
/
397
< prev
next >
Wrap
Text File
|
1996-08-06
|
7KB
|
163 lines
Path: solon.com!not-for-mail
From: msb@sq.com
Newsgroups: comp.std.c,comp.lang.c.moderated
Subject: Re: Integral promotion.
Date: 15 Feb 1996 08:44:06 -0600
Organization: SoftQuad Inc., Toronto, Canada
Sender: clc@solutions.solon.com
Approved: clc@solutions.solon.com
Message-ID: <4fvgrm$dv9@solutions.solon.com>
References: <4fstj7$2l6@solutions.solon.com> <4fu835$9de@solutions.solon.com>
NNTP-Posting-Host: solutions.solon.com
Oh dear, I have to correct Chris Torek (torek@bsdi.com). Most of his
article was correct, of course; please review it if you need more context.
The question was about
> > short test(short x1, short x2)
> > {
> > short result;
> > result = x1 + x2; /* Warning: '=' : conversion from 'int '
> > to 'short ', possible loss of data */
> > return result;
> > }
Chris writes:
> For efficiency and/or less-surprising behavior, ANSI C says that
> prototyped parameters (as you have here) are not promoted. (Many
> compilers simply promote them at the caller, and then narrow them
> before entering the function, just as for Old C, but they can now
> avoid these steps in this case.)
It is correct that prototyped arguments do not undergo the default
*argument* promotions, but they still undergo the *integral* promotions.
Thus even in ANSI C, if this function is called this way:
extern short p, q, r, test(short,short);
r = test(p,q);
the values from p and q are still converted from short to int and back
to short. However, there is a difference from old C. In old C, what
happened was that p and q were converted to int in the caller, two ints
were given to test(), and these int values were *then* converted back to
shorts to assign them to x1 and x2. In ANSI C, with the prototype in
scope, p and q are converted to int and then *immediately* back to
short; two shorts are passed to test(), and these values are assigned
to x1 and x2.
And this difference is significant, because the caller and test() may
be compiled separately. This means that in old C, the compiler would
*have* to perform the conversions; but in ANSI C, because the conversion
is reversed within the same expression, it is trivial for the compiler
to optimize it out.
In the code originally posted, the call was test(1,2). Since the
arguments in this case are ints, a conversion is always required.
This is different from the case of an argument of type float. In old C,
just as shorts were automatically promoted to int anywhere, so floats
were to double. In ANSI C, *this* automatic promotion has been completely
removed -- except when it forms part of the default argument promotions.
This paragraph of Chris's is worth reemphasizing:
> Because ANSI C requires promotion for non-prototypes and non-promotion
> for prototypes, it is important to make sure you never mix up the
> two kinds of declarations and definitions when you use `narrow'
> types (char, short, signed char, unsigned char, unsigned short,
> and float).
Now to the inside of the function.
> result = x1 + x2;
> ... if `int' and `short' are the same width, a result that would
> be outside the range [SHRT_MIN .. SHRT_MAX] is also outside the
> range [INT_MIN .. INT_MAX], and the behavior is undefined (ANSI C
> section 3.3, p. 39, ll. 15--17).
Right. If int and short are the same width, overflow can occur, with
underfined behavior.
> On the other hand, if `int's are wider than `short's ... the sum is
> not outside the representable range ... The final step is to convert
> the sum back to type `short'. Here things get a little fuzzy....
No, they don't.
> comp.std.c has had arguments as to what happens if the sum is `int',
> `int' is wider than `short', and the sum is not representable as a `short'.
Only arguments between -- to be blunt -- people who know what they are
talking about and those who do not. Needless to say, Chris is usually
in the former category, but not this time. The result of the conversion
in this case is implementation-defined, and undefined behavior does not
occur. Chapter and verse below.
> The Standard distinguishes between `implicit' and `explicit'
> conversions ...
It makes no distinction whatever between their semantics, except that in
certain cases "that involve pointers" (quote from 6.3.4/3.3.4), it requires
the conversions to be explicit. We're not talking about pointers here.
> Some have argued that an explicit conversion should never cause an
> overflow, but there seems to be no wording in the standard to justify
> this position. There appears to be agreement that implicit conversion
> might overflow. The behavior on overflow would be undefined.
Completely wrong, assuming that "overflow" is meant to imply an error
condition. It might be sensible, but it's not what the standard specifies.
Chapter and verse below.
> It is interesting, however, to note that if overflow will ever occur,
> it will occur no matter whether the ranges of short and int coincide
> or not. The only thing that changes here is the exact operation that
> overflows: the sum, or the implicit conversion in the assignment.
This would be correct if "overflow" is *not* taken as implying an error
condition, but merely the occurrence of a value that will not fit.
Excerpts from the standard:
* From 6.3.16.1/3.3.16.1:
# In "simple assignment" (=), the value of the right operand is
# converted to the type of the left operand ...
* From 6.2.1.2/3.2.1.2:
# When a value with integral type is demoted to a signed integer with
# smaller size ... if the value cannot be represented the result is
# implementation-defined.
Hence implementation-defined behavior occurs.
* From 3.10/1.6 (emphasis added):
# Implementation-defined behavior -- behavior, for a CORRECT program
# construct and CORRECT data, that depends on the characteristics of
# the implementation and that each implementation shall document.
* From 3.16/1.6 (emphasis added):
# Undefined behavior -- behavior, upon use of a NONPORTABLE OR ERRONEOUS
# program construct, of ERRONEOUS data, or of indeterminately-valued
# objects, for which the Standard imposes no requirements. ...
Hence implementation-defined and undefined behavior are mutually
exclusive; the occurrence of implementation-defined behavior implies
that the construct is correct; and so, in the case in question where
int is wider than short, undefined behavior cannot occur.
--
Mark Brader "Nicely self-consistent. (Pay no attention to
msb@sq.com that D-floating number behind the curtain!)"
SoftQuad Inc., Toronto -- Chris Torek, on pasta
My text in this article is in the public domain.